#include "GameLevel.hpp"
#include "LevelSector.hpp"
#include "VirtualSector.hpp"
#include "../ResourceManagers/Mesh3D.hpp"
#include "../SceneManager.hpp"
#include <assert.h>
#include <fstream>
#include <iostream>
#include <math.h>

using namespace std;

GameLevel::GameLevel(std::string levelFileName, float fMargin, float fFOV)
{
	m_fMargin = fMargin;
	m_fFOV    = fFOV;

	loadLVF(levelFileName);

	calcDepth();
	calcWidth();
	calcHeight();
}

GameLevel::~GameLevel()
{
}

MVector3D& GameLevel::getCameraStartPos()
{
	return m_camStartPos;
}

MVector3D& GameLevel::getCameraStopPos()
{
	return m_camStopPos;
}

void GameLevel::calcDepth()
{
	float fMinZ, fMaxZ, fTempMaxZ, fTempMinZ;

	fMinZ = 0xFFFF;
	fMaxZ = -fMinZ;

	vector<LevelSector*>::iterator sectorsBegin = m_sectors.begin();
	vector<LevelSector*>::iterator sectorsEnd   = m_sectors.end();

	for(sectorsBegin; sectorsBegin != sectorsEnd; ++sectorsBegin)
	{
		fTempMaxZ = const_cast<Mesh3D*>((*sectorsBegin)->getMesh())->getMaximum(Z_AXIS);
		if(fTempMaxZ > fMaxZ)
			fMaxZ = fTempMaxZ;

		fTempMinZ = const_cast<Mesh3D*>((*sectorsBegin)->getMesh())->getMinimum(Z_AXIS);
		if(fTempMinZ < fMinZ)
			fMinZ = fTempMinZ;
	}

	m_fLvlDepth = fMaxZ - fMinZ;
}

void GameLevel::calcHeight()
{
	float fMinY, fMaxY, fTempMaxY, fTempMinY;

	fMinY = 0xFFFF;
	fMaxY = -fMinY;

	vector<LevelSector*>::iterator sectorsBegin = m_sectors.begin();
	vector<LevelSector*>::iterator sectorsEnd   = m_sectors.end();

	for(sectorsBegin; sectorsBegin != sectorsEnd; ++sectorsBegin)
	{
		fTempMaxY = const_cast<Mesh3D*>((*sectorsBegin)->getMesh())->getMaximum(Y_AXIS);
		if(fTempMaxY > fMaxY)
			fMaxY = fTempMaxY;

		fTempMinY = const_cast<Mesh3D*>((*sectorsBegin)->getMesh())->getMinimum(Y_AXIS);
		if(fTempMinY < fMinY)
			fMinY = fTempMinY;
	}

	m_fLvlHeight = fMaxY - fMinY;
}

void GameLevel::calcWidth()
{
	float fMinX, fMaxX, fTempMaxX, fTempMinX;

	fMinX = 0xFFFF;
	fMaxX = -fMinX;

	vector<LevelSector*>::iterator sectorsBegin = m_sectors.begin();
	vector<LevelSector*>::iterator sectorsEnd   = m_sectors.end();

	for(sectorsBegin; sectorsBegin != sectorsEnd; ++sectorsBegin)
	{
		fTempMaxX = const_cast<Mesh3D*>((*sectorsBegin)->getMesh())->getMaximum(X_AXIS);
		if(fTempMaxX > fMaxX)
			fMaxX = fTempMaxX;

		fTempMinX = const_cast<Mesh3D*>((*sectorsBegin)->getMesh())->getMinimum(X_AXIS);
		if(fTempMinX < fMinX)
			fMinX = fTempMinX;
	}

	m_fLvlWidth = fMaxX - fMinX;
}

void GameLevel::calcCameraStartPos()
{
	float fCamY = 0.0f;

	fCamY = ((m_fLvlWidth/2.0f) - m_fMargin)/tan(m_fFOV/2.0);

	m_camStartPos.x = 0.0f;
	m_camStartPos.y = fCamY;
	m_camStartPos.z = m_fLvlWidth/2.0f;
}

void GameLevel::calcCameraStopPos()
{
	float fCamY = 0.0f;

	fCamY = ((m_fLvlWidth/2.0f) - m_fMargin)/tan(m_fFOV/2.0);

	m_camStopPos.x = 0.0f;
	m_camStopPos.y = fCamY;
	m_camStopPos.z = m_fLvlDepth - (m_fLvlWidth/2.0f);
}

bool GameLevel::loadLVF(std::string lvfFileName)
{
	bool bSuccess = false;

	ifstream  levelFile;
	int iFileSize, iNumSectors;
	char* pFileContent;
	char* offset;

	levelFile.open(lvfFileName.c_str(), ios::in | ios::binary);
	
	if(!levelFile.is_open())
		return bSuccess;

	levelFile.seekg(0, ios::end);
	iFileSize = levelFile.tellg();
	pFileContent = new char[iFileSize];
	levelFile.seekg(0, ios::beg);

	levelFile.read(pFileContent, iFileSize);
	levelFile.close();

	offset = pFileContent;

	//! Check if valid LVF file.
	if(pFileContent[0] != 'L' || pFileContent[1] != 'V' || pFileContent[2] != 'F')
		return bSuccess;
	else
		offset += 3;

	//! Read number of sectors
	memcpy(&iNumSectors, offset, sizeof(int));
	offset += sizeof(int);

	float nextTrans = 0.0f;
	for(int i = 0; i < iNumSectors; ++i)
	{
		LevelSector*  pSector = NULL;
		char*         pName;
		char*         pTexName;
		int           iLen, iNumUnits, iNumStruct;

		
		//sector name len
		memcpy(&iLen, offset, sizeof(int));
		offset += sizeof(int);

		//sector name
		pName = new char[iLen+1];
		memcpy(pName, offset, iLen);
		pName[iLen] = '\0';
		offset += iLen;

		//sector tex name len
		memcpy(&iLen, offset, sizeof(int));
		offset += sizeof(int);

		//sector tex name
		pTexName = new char[iLen+1];
		memcpy(pTexName, offset, iLen);
		pTexName[iLen] = '\0';
		offset += iLen;

		pSector = new LevelSector(pName, pTexName);

		//num of units for this sector
		memcpy(&iNumUnits, offset, sizeof(int));
		offset += sizeof(int);

		//num of structures for this sector
		memcpy(&iNumStruct, offset, sizeof(int));
		offset += sizeof(int);

		for(int i = 0; i < iNumUnits; ++i)
		{
			float fSpeed;
			float pos[3];
			int iLen;
			char *pUnitName, *pUTexName;

			//unit name len
			memcpy(&iLen, offset, sizeof(int));
			offset += sizeof(int);

			//unit name
			pUnitName = new char[iLen+1];
			memcpy(pUnitName, offset, iLen);
			pUnitName[iLen] = '\0';
			offset += iLen;

			//tex name len
			memcpy(&iLen, offset, sizeof(int));
			offset += sizeof(int);

			//tex name
			pUTexName = new char[iLen+1];
			memcpy(pUTexName, offset, iLen);
			pUTexName[iLen] = '\0';
			offset += iLen;

			//pos
			memcpy(pos, offset, 3*sizeof(float));
			offset += 3*sizeof(float);

			//speed
			memcpy(&fSpeed, offset, sizeof(float));
			offset += sizeof(float);

			pSector->createUnit(pUnitName, pUTexName, pos, fSpeed);
		}

		for(int i = 0; i < iNumStruct; ++i)
		{
			float pos[3];
			int iLen;
			char *pStructName, *pUTexName;

			//unit name len
			memcpy(&iLen, offset, sizeof(int));
			offset += sizeof(int);

			//unit name
			pStructName = new char[iLen+1];
			memcpy(pStructName, offset, iLen);
			pStructName[iLen] = '\0';
			offset += iLen;

			//tex name len
			memcpy(&iLen, offset, sizeof(int));
			offset += sizeof(int);

			//tex name
			pUTexName = new char[iLen+1];
			memcpy(pUTexName, offset, iLen);
			pUTexName[iLen] = '\0';
			offset += iLen;

			//pos
			memcpy(pos, offset, 3*sizeof(float));
			offset += 3*sizeof(float);


			pSector->createStructure(pStructName, pUTexName, pos);
		}

		pSector->setPosition(MVector3D(0.0f, 0.0f, nextTrans));
		m_sectors.push_back(pSector);
		SceneManager::GetInstance()->pushSector(pSector);

		nextTrans -= abs(const_cast<Mesh3D*>(pSector->getMesh())->getMaximum(Z_AXIS) - const_cast<Mesh3D*>(pSector->getMesh())->getMinimum(Z_AXIS));

		//delete[] pTexName;
	}

	bSuccess = true;
	return bSuccess;
}


//EOF
